<?php
require_once 'lib/phpunit/TestCase.php';
require_once "lib/bigfoot/easyrdf.php";
require_once "lib/arc/ARC_rdfxml_parser.php";
require_once "lib/arc/ARC_simple_model.php";

class ThingTest extends PHPUnit_TestCase {
    var $_single_literal_value =  '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
	<rdf:Description rdf:about="http://example.org/subj">
    <ex:pred>scooby</ex:pred>
  </rdf:Description>
</rdf:RDF>';

    var $_single_literal_value_triples = array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'scooby') ) );

    var $_two_literal_values =  '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
	<rdf:Description rdf:about="http://example.org/subj">
    <ex:pred>scooby</ex:pred>
    <ex:pred>shaggy</ex:pred>
  </rdf:Description>
</rdf:RDF>';

    var $_two_literal_values_triples = array( 
         array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'scooby') ),
         array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'shaggy') ),
       );

    var $_two_literal_triples =  '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
	<rdf:Description rdf:about="http://example.org/subj">
    <ex:pred>scooby</ex:pred>
    <ex:pred2>shaggy</ex:pred2>
  </rdf:Description>
</rdf:RDF>';

    var $_two_related_triples_by_uri =  '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
	<rdf:Description rdf:about="http://example.org/subj">
    <ex:pred>scooby</ex:pred>
    <ex:link rdf:resource="http://example.org/subj2" />
  </rdf:Description>
	<rdf:Description rdf:about="http://example.org/subj2">
    <ex:pred2>shaggy</ex:pred2>
  </rdf:Description>
</rdf:RDF>';

    var $_two_related_triples_with_cycle =  '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
	<rdf:Description rdf:about="http://example.org/subj">
    <ex:pred>scooby</ex:pred>
    <ex:link rdf:resource="http://example.org/subj2" />
  </rdf:Description>
	<rdf:Description rdf:about="http://example.org/subj2">
    <ex:pred2>shaggy</ex:pred2>
    <ex:link rdf:resource="http://example.org/subj" />
  </rdf:Description>
</rdf:RDF>';

    var $_single_blank_value =  '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
	<rdf:Description rdf:about="http://example.org/subj">
    <ex:pred rdf:nodeID="foo" />
  </rdf:Description>
</rdf:RDF>';


    var $_single_blank_subject =  '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ex="http://example.org/">
	<rdf:Description rdf:nodeID="foo">
    <ex:pred>scooby</ex:pred>
  </rdf:Description>
</rdf:RDF>';


  function setUp() {
      $parser_args=array(
        "bnode_prefix"=>"genid",
        "base"=>""
      );
      $this->_parser =new ARC_rdfxml_parser($parser_args);
  }

  function test_uri() {
    $thing = new Thing('http://example.org/subj');
    $this->assertEquals( 'http://example.org/subj', $thing->get_uri() );
  }

  function test_properties_are_in_an_array() {
    $thing = new Thing('http://example.org/subj');  
    $this->assertTrue( is_array( $thing->_properties)  );
  }

  function test_properties_are_available() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertEquals( 1, count($thing->_properties) );
  }


  function test_literals_are_parsed() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertTrue( array_key_exists('http://example.org/pred', $thing->_properties)  );
    $this->assertTrue( is_array($thing->_properties['http://example.org/pred'])  );
    $this->assertEquals( 'scooby', $thing->_properties['http://example.org/pred'][0]  );
  }

  function test_multiple_literal_values_are_parsed() {
    $triples = $this->_parser->parse_data( $this->_two_literal_values );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertEquals( 1, count($thing->_properties) );

    $this->assertTrue( array_key_exists('http://example.org/pred', $thing->_properties)  );
    $this->assertTrue( is_array($thing->_properties['http://example.org/pred'])  );
    $this->assertTrue( in_array('scooby', $thing->_properties['http://example.org/pred'] )  );
    $this->assertTrue( in_array('shaggy', $thing->_properties['http://example.org/pred'] )  );
  }

  function test_multiple_literal_triples_are_parsed() {
    $triples = $this->_parser->parse_data( $this->_two_literal_triples );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertEquals( 2, count($thing->_properties) );

    $this->assertTrue( array_key_exists('http://example.org/pred', $thing->_properties)  );
    $this->assertTrue( is_array($thing->_properties['http://example.org/pred'])  );
    $this->assertTrue( in_array('scooby', $thing->_properties['http://example.org/pred'] )  );

    $this->assertTrue( array_key_exists('http://example.org/pred2', $thing->_properties)  );
    $this->assertTrue( is_array($thing->_properties['http://example.org/pred2'])  );
    $this->assertTrue( in_array('shaggy', $thing->_properties['http://example.org/pred2'] )  );
  }

  function test_properties_become_variables() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred') ) );
    $thing->read_from_triple_array( $triples );
    
    $this->assertTrue( is_array($thing->pred)  );
    $this->assertEquals( 'scooby', $thing->pred[0]  );
  }

  function test_property_prefixes_become_variables() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_prefix_mapping( array( 'http://example.org/'=>'ex') );
    $thing->read_from_triple_array( $triples );
    
    $this->assertTrue( is_array($thing->ex_pred)  );
    $this->assertEquals( 'scooby', $thing->ex_pred[0]  );
  }

  function test_functional_properties_are_not_arrays() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred', 'functional'=>true) ) );
    $thing->read_from_triple_array( $triples );
    
    $this->assertFalse( is_array($thing->pred)  );
    $this->assertEquals( 'scooby', $thing->pred  );
  }

  function test_other_subjects_are_ignored() {
    $triples = $this->_parser->parse_data( $this->_two_related_triples_by_uri );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertTrue( array_key_exists('http://example.org/pred', $thing->_properties)  );
    $this->assertFalse( array_key_exists('http://example.org/pred2', $thing->_properties)  );
  }


  function test_resources_are_parsed() {
    $triples = $this->_parser->parse_data( $this->_two_related_triples_by_uri );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertTrue( array_key_exists('http://example.org/link', $thing->_properties)  );
    $this->assertTrue( is_array($thing->_properties['http://example.org/link'])  );
    $this->assertTrue( is_a( $thing->_properties['http://example.org/link'][0], 'Thing' )  );
  }

  function test_linked_resources_are_populated() {
    $triples = $this->_parser->parse_data( $this->_two_related_triples_by_uri );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertTrue( array_key_exists('http://example.org/link', $thing->_properties)  );
    $this->assertTrue( is_array($thing->_properties['http://example.org/link'])  );
    $this->assertEquals( 'shaggy', $thing->_properties['http://example.org/link'][0]->_properties['http://example.org/pred2'][0] ) ;
  }


  function test_linked_resources_avoid_circular_references() {
    $triples = $this->_parser->parse_data( $this->_two_related_triples_with_cycle );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertTrue( array_key_exists('http://example.org/link', $thing->_properties)  );
    $this->assertTrue( is_array($thing->_properties['http://example.org/link'])  );
    $this->assertEquals( 'shaggy', $thing->_properties['http://example.org/link'][0]->_properties['http://example.org/pred2'][0] ) ;
  }

  function test_root_resources_exists_only_once() {
    $triples = $this->_parser->parse_data( $this->_two_related_triples_with_cycle );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );

    $thing->_properties['http://example.org/link'][0]->_properties['http://example.org/link'][0]->_properties['http://example.org/pred'][0] = 'velma';
    $this->assertEquals( 'velma', $thing->_properties['http://example.org/pred'][0] ) ;
  }

  function test_linked_resources_use_same_property_definitions() {
    $triples = $this->_parser->parse_data( $this->_two_related_triples_by_uri );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/link'=>array( 'name'=>'link'),  'http://example.org/pred2'=>array( 'name'=>'pred2') ) );
    $thing->read_from_triple_array( $triples );
    
    $linked = $thing->link[0];
    
    $this->assertEquals( 'shaggy', $linked->pred2[0] ) ;
  }


  function test_blank_resources_are_parsed() {
    $triples = $this->_parser->parse_data( $this->_single_blank_value );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $this->assertTrue( array_key_exists('http://example.org/pred', $thing->_properties)  );
    $this->assertTrue( is_array($thing->_properties['http://example.org/pred'])  );
    $this->assertTrue( is_a( $thing->_properties['http://example.org/pred'][0], 'Thing' )  );
    $this->assertEquals( "_:foo",  $thing->_properties['http://example.org/pred'][0]->_uri );
  }

  function test_to_triple_array_for_single_literal_value() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $result = $thing->to_triple_array();

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 1, count($result) );
    $this->assertEquals( 'uri', $result[0]['s']['type'] );
    $this->assertEquals( 'http://example.org/subj', $result[0]['s']['uri'] );
    $this->assertEquals( 'http://example.org/pred', $result[0]['p'] );

    $this->assertEquals( 'literal', $result[0]['o']['type'] );
    $this->assertEquals( 'scooby', $result[0]['o']['val'] );
    $this->assertEquals( 0, count( array_diff( $result, $this->_single_literal_value_triples) ) );
  }

  function test_to_triple_array_for_two_literal_values() {
    $triples = $this->_parser->parse_data( $this->_two_literal_values );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $result = $thing->to_triple_array();

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 2, count($result) );

    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'scooby') ), $result)  );
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'shaggy') ), $result)  );
    // $this->assertEquals( 0, count( array_diff( $result, $this->_two_literal_values_triples) ) );
    // $this->assertEquals( 0, count( array_diff( $this->_two_literal_values_triples, $result) ) );


  }

  function test_to_triple_array_for_two_literal_triples() {
    $triples = $this->_parser->parse_data( $this->_two_literal_triples );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $result = $thing->to_triple_array();

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 2, count($result) );

    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'scooby') ), $result)  );
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred2', 'o'=>array('type'=>'literal', 'val'=>'shaggy') ), $result)  );
    // $this->assertEquals( 0, count( array_diff( $result, $this->_two_literal_values_triples) ) );
    // $this->assertEquals( 0, count( array_diff( $this->_two_literal_values_triples, $result) ) );
  }

  function test_to_triple_array_for_two_uri_subjects() {
    $triples = $this->_parser->parse_data( $this->_two_related_triples_by_uri );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $result = $thing->to_triple_array();
    // $this->assertEquals( 'foo', print_r ($thing->_property_map, true) );
    // $this->assertEquals( 'foo', print_r ($result, true) );
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/link', 'o'=>array('type'=>'uri', 'uri'=>'http://example.org/subj2') ), $result)  );
  }

  function test_to_triple_array_for_blank_value() {
    $triples = $this->_parser->parse_data( $this->_single_blank_value );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $result = $thing->to_triple_array();
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'bnode', 'bnode_id'=>'_:foo') ), $result)  );
  }

  function test_to_triple_array_for_blank_subject() {
    $triples = $this->_parser->parse_data( $this->_single_blank_subject );
    $thing = new Thing('_:foo');
    $thing->read_from_triple_array( $triples );
    
    $result = $thing->to_triple_array();
    $this->assertTrue( in_array( array('s' => array('bnode_id'=>'_:foo', 'type'=>'bnode'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'scooby') ), $result)  );
  }

  function test_to_triple_array_property_mapping_with_modified_value() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred') ) );
    $thing->read_from_triple_array( $triples );
    
    $thing->pred[0] = 'shaggy';

    $result = $thing->to_triple_array();
    // $this->assertEquals( '$result', print_r ($result, true) );

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 1, count($result) );
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'shaggy') ), $result)  );
  }

  function test_to_triple_array_property_prefix_with_modified_value() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_prefix_mapping( array( 'http://example.org/'=>'ex') );
    $thing->read_from_triple_array( $triples );
    
    $thing->ex_pred[0] = 'shaggy';

    $result = $thing->to_triple_array();
    // $this->assertEquals( '$result', print_r ($result, true) );

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 1, count($result) );
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'shaggy') ), $result)  );
  }

  function test_to_triple_array_property_mapping_with_added_value() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred') ) );
    $thing->read_from_triple_array( $triples );
    
    $thing->pred[] = 'shaggy';

    $result = $thing->to_triple_array();
    // $this->assertEquals( '$result', print_r ($result, true) );

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 2, count($result) );
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'shaggy') ), $result)  );
  }

  function test_variables_are_created_even_when_no_values() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred') ) );
    

    $this->assertTrue( is_array($thing->pred)  );
    $this->assertEquals( 0, count($thing->pred) );
  }

  function test_variables_are_created_even_when_no_values_and_is_functional() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred', 'functional'=>true) ) );
    

    $this->assertFalse( is_array($thing->pred)  );
    $this->assertEquals( null, $thing->pred );
  }

  function test_variables_with_no_value_not_included_in_triples() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/bogus'=>array( 'name'=>'bogus', 'functional'=>true) ) );
    $thing->read_from_triple_array( $triples );
    
    $result = $thing->to_triple_array();

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 1, count($result) );
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'literal', 'val'=>'scooby') ), $result)  );
  }

  function test_to_rdfxml() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->read_from_triple_array( $triples );
    
    $result = $this->_parser->parse_data( $thing->to_rdfxml() );
    
    $this->assertEquals(count( $triples ),  count( $result ) );
  }
  


  function test_to_triple_array_property_mapping_with_added_resource() {
    $triples = $this->_parser->parse_data( $this->_single_literal_value );
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred') ) );
    $thing->read_from_triple_array( $triples );
    
    $thing->pred[] = new Thing('http://example.org/other');

    $result = $thing->to_triple_array();
    // $this->assertEquals( '$result', print_r ($result, true) );

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 2, count($result) );
    $this->assertTrue( in_array( array('s' => array('uri'=>'http://example.org/subj', 'type'=>'uri'), 'p'=>'http://example.org/pred', 'o'=>array('type'=>'uri', 'uri'=>'http://example.org/other') ), $result)  );
  }

  function test_make_thing_sets_uri() {
    $thing = new Thing('http://example.org/subj');
    $other = $thing->make_thing('http://example.org/other');

    $this->assertTrue( is_a($other, 'Thing')  );
    $this->assertEquals( 'http://example.org/other', $other->get_uri() );
  }

  function test_make_thing_sets_property_mapping() {
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred') ) );
    $other = $thing->make_thing('http://example.org/other');


    list($propertyName, $propertyFunctional) = $other->get_property_details('http://example.org/pred');
    $this->assertEquals( 'pred',$propertyName );
  }

  function test_make_thing_sets_prefix_mapping() {
    $thing = new Thing('http://example.org/subj');
    $thing->set_prefix_mapping( array( 'http://example.org/'=>'ex') );
    $other = $thing->make_thing('http://example.org/other');
    list($propertyName, $propertyFunctional) = $other->get_property_details('http://example.org/pred');
    $this->assertEquals( 'ex_pred',$propertyName );
  }


  function test_to_triple_array_ignores_duplicate_triples() {
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred'), 'http://example.org/link'=>array( 'name'=>'link'), 'http://example.org/link2'=>array( 'name'=>'link2')  ) );
    
    $other = new Thing('http://example.org/other');
    $other->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred'), 'http://example.org/link'=>array( 'name'=>'link'), 'http://example.org/link2'=>array( 'name'=>'link2')  ) );
    $other->pred[] = 'scooby';


    $thing->link[] = $other;
    $thing->link2[] = $other;

    $result = $thing->to_triple_array();
    // $this->assertEquals( '$result', print_r ($result, true) );

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 3, count($result) );
  }

  function test_to_triple_array_ignores_duplicate_triples_for_different_instances_of_thing() {
    $thing = new Thing('http://example.org/subj');
    $thing->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred'), 'http://example.org/link'=>array( 'name'=>'link'), 'http://example.org/link2'=>array( 'name'=>'link2')  ) );
    
    $other = new Thing('http://example.org/other');
    $other->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred'), 'http://example.org/link'=>array( 'name'=>'link'), 'http://example.org/link2'=>array( 'name'=>'link2')  ) );
    $other->pred[] = 'scooby';

    $other2 = new Thing('http://example.org/other');
    $other2->set_property_mapping( array( 'http://example.org/pred'=>array( 'name'=>'pred'), 'http://example.org/link'=>array( 'name'=>'link'), 'http://example.org/link2'=>array( 'name'=>'link2')  ) );
    $other2->pred[] = 'scooby';

    $thing->link[] = $other;
    $thing->link2[] = $other2;

    $result = $thing->to_triple_array();
    // $this->assertEquals( '$result', print_r ($result, true) );

    $this->assertTrue( is_array($result)  );
    $this->assertEquals( 3, count($result) );
  }


}

